package com.smallx.xproc.tools;

import java.util.*;
import java.util.logging.*;
import java.io.*;
import java.net.*;

public abstract class CommandLine implements Runnable {

static String fineLog = 
"handlers= java.util.logging.ConsoleHandler\n"+
".level= FINE\n"+
"java.util.logging.ConsoleHandler.level = FINE\n"+
"java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter\n";
;
static String finerLog = 
"handlers= java.util.logging.ConsoleHandler\n"+
".level= FINER\n"+
"java.util.logging.ConsoleHandler.level = FINER\n"+
"java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter\n";
;
static String finestLog = 
"handlers= java.util.logging.ConsoleHandler\n"+
".level= FINEST\n"+
"java.util.logging.ConsoleHandler.level = FINEST\n"+
"java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter\n";
;
   public void setLogLevel(Level level) {
      /*
      Logger log = Logger.getLogger("com.smallx");
      log.setLevel(level);
      log = Logger.getLogger("org.infoset");
      log.setLevel(level);
       */
      if (level==Level.FINE) {
         try {
            LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(fineLog.getBytes()));
         } catch (java.io.IOException ex) {
            ex.printStackTrace();
         }
      } else if (level==Level.FINER) {
         try {
            LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(finerLog.getBytes()));
         } catch (java.io.IOException ex) {
            ex.printStackTrace();
         }
      } else if (level==Level.FINEST) {
         try {
            LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(finestLog.getBytes()));
         } catch (java.io.IOException ex) {
            ex.printStackTrace();
         }
         
      }
   }
   public static URI toURI(String uristr) 
      throws URISyntaxException
   {
      // A bang signals that the argument is an absolute URI
      if (uristr.charAt(0)=='!') {
         return new URI(uristr.substring(1));
      } else if (uristr.startsWith("file:") || uristr.startsWith("http:")) {
         return new URI(uristr);
      } else {
         File f = new File(uristr);
         return f.toURI();
      }
   }

   static Integer SPECIFIED = new Integer(1);
   static Integer FLAG = new Integer(-1);
   static Integer SINGLE = new Integer(-2);
   static Integer LIST = new Integer(-3);
   Map options;
   int count;
   boolean allowMore;
   
   protected String [] arguments;
   protected int statusCode;

   protected CommandLine() {
      options = new HashMap();
      count = 0;
      allowMore = false;
   }

   protected void allowOption(String name) {
      options.put(name,FLAG);
   }

   protected void allowOptionWithArgument(String name) {
      options.put(name,SINGLE);
   }

   protected void allowOptionWithArguments(String name) {
      options.put(name,LIST);
   }

   protected void setMinimumArguments(int count) {
      this.count = count;
   }

   protected void allowMoreArguments() {
      allowMore = true;
   }

   protected boolean hasOption(String name) {
      Object value = options.get(name);
      if (value instanceof Integer) {
         return value.equals(SPECIFIED);
      } else {
         return true;
      }
   }

   protected String getOption(String name) {
      Object value = options.get(name);
      if (value!=null && !(value instanceof String)) {
         value = null;
      }
      return (String)value;
   }

   protected Iterator<String> getOptionValues(String name) {
      Object value = options.get(name);
      if (value instanceof List) {
         return ((List<String>)value).iterator();
      }
      return null;
   }
  
   protected abstract void usage();

   protected abstract void setup();

   protected abstract int runProgram();
   
   protected String [] getArguments() {
      return arguments;
   }
   
   protected void reportError(String msg) {
      System.err.println(msg);
   }
   
   public void run() {
      statusCode = runProgram();
   }
   
   public int getStatusCode() {
      return statusCode;
   }

   public boolean parseArguments(String [] args) {

      setup();

      int argIndex = 0;
      while (argIndex<args.length && args[argIndex].charAt(0)=='-') {
         String optionName = args[argIndex].substring(1);
         if (optionName.equals("h") || optionName.equals("help") || optionName.equals("?")) {
            usage();
            return false;
         }
         if (optionName.equals("l")) {
            if ((argIndex+1)>=args.length) {
               reportError("The "+optionName+" option requires a parameter.");
               return false;
            }
            argIndex++;
            if (args[argIndex].equals("info")) {
               setLogLevel(Level.INFO);
            } else if (args[argIndex].equals("fine")) {
               setLogLevel(Level.FINE);
            } else if (args[argIndex].equals("finer")) {
               setLogLevel(Level.FINER);
            } else if (args[argIndex].equals("finest")) {
               setLogLevel(Level.FINEST);
            } else {
               reportError("Parameter value "+args[argIndex]+" to option "+optionName+" is not allowed.");
               return false;
            }
            argIndex++;
            continue;
         }
         Object value = options.get(optionName);
         if (value==null) {
            reportError("Unknown option "+optionName);
            return false;
         }
         if (value instanceof Integer) {
            if (((Integer)value).intValue()<FLAG.intValue()) {
               if ((argIndex+1)>=args.length) {
                  reportError("The "+optionName+" option requires a parameter.");
                  return false;
               }
               argIndex++;
               if (value.equals(SINGLE)) {
                  options.put(optionName,args[argIndex]);
               } else {
                  List<String> values = new ArrayList<String>();
                  options.put(optionName,values);
                  values.add(args[argIndex]);
               }
            } else {
               options.put(optionName,SPECIFIED);
            }
         } else if (value instanceof List) {
            argIndex++;
            List<String> values = (List<String>)value;
            values.add(args[argIndex]);
         } else {
            reportError("The "+optionName+" option has already been specified.");
            return false;
         }
         argIndex++;
      }
      int argNumber = args.length-argIndex;
      if (argNumber<count) {
         reportError(allowMore ? "A minimum of "+count+" arguments must be specified.  See usage." : "There must be "+count+" number of arguments.  See usage.");
         return false;
      }
      if (!allowMore && argNumber>count) {
         reportError("There may be only "+count+" arguments specified.  See usage.");
         return false;
      }
      arguments = new String[argNumber];
      System.arraycopy(args,argIndex,arguments,0,argNumber);
      return true;
   }

}
